#pragma once

/** 本库用于存放一些通用的网络函数*/

// 域名转ip
int hostToIp(const std::string &, std::string &);

// 网址或点分十进制ip地址转网络序ip
int addrToNs(const std::string &, int &);

// 客户端固定端口
int clientBindStaticPort(int, short);

// 创建servaddr结构体，传入点分十进制ip和主机序端口号
int createIpv4ServAddr(const std::string &, const short, void *);
// 创建servaddr结构体，传入网络序ip和网络序端口号
int createIpv4ServAddr(const int, const short, void *);

/*******************************************************
 * SocketBase类
 * 类说明：socket基础类
 * 功能：
 * 1.定义socket：SocketBase()
 * 2.对socket的工作方式进行设置，如同步/异步，缓冲大小等等
 * 3.获取socket的一系列值
 * 4.对socket进行读写操作
 **************************************************** **/
class SocketBase
{
protected:
    int sock;                     // 套接字文件描述符
    int is_async;                 // 是否异步
    struct sockaddr_in sock_addr; // sock地址

    friend class SelectSocket;

    int setPortReuse(); // 设置端口可以重复使用，默认开启

public:
    // 注意：构造函数中不创建socket，因为accept通讯socket的文件描述符不是由socket函数创建的
    SocketBase();

    // 创建socket
    void initSocket();
    // 关闭socket
    void closeSocket();

    // 设置socket非阻塞
    int setAsync();
    // 设置socket读缓冲大小，单位字节
    int setRecvBuf(int);
    // 设置socket写缓冲大小，单位字节
    int setSendBuf(int);
    // 设置socket读返回的最低限制，单位字节
    int setRecvLow(int);
    // 设置socket写返回的最低限制，单位字节
    int setSendLow(int);

    /* 由于读写也算是基本功能，且双端都要使用，故封装至SocketBase中 */
    // 同步读
    int readSockSync(char *, int);
    // 异步读
    int readSockAsync(char *, int, struct timeval * = NULL);
    // 同步写
    int writeSockSync(char *, int);
    // 异步写
    int writeSockAsync(char *, int, struct timeval * = NULL);
    // 读取socket数据至数组中，长度为len，如果是异步则支持传入超时时间，默认为无限
    int readSock(char *, int, struct timeval * = NULL);
    // 将数组数据写入至socket，长度为len，如果是异步则支持传入超时时间，默认为无限
    int writeSock(char *, int, struct timeval * = NULL);

    // 获取sock
    int getSock() const;
    // 获取socket读缓冲大小，单位字节，返回值表示是否读取成功，传入参数为缓冲大小
    int getRecvBuf(int &) const;
    // 获取socket写缓冲大小，单位字节，返回值表示是否读取成功，传入参数为缓冲大小
    int getSendBuf(int &) const;
    // 获取点分十进制ip值（用于调试）
    const char *getIp() const;
    // 获取主机序port（用于调试）
    int getPort() const;

    // 静态方法
    // 设置忽略SIGPIPE错误
    static void setIgnSIGPIPE();

    ~SocketBase();
};

/*******************************************************
 * SelectSocket类
 * 类说明：select的简易封装
 * 功能：
 * 1.设置监听文件描述符：set开头的方法
 * 2.取消监听文件描述符：clear开头的方法
 * 3.查看某个文件描述符是否在set中：check开头的方法
 * 4.重置类对读/写/异常的监听：reset开头的方法
 * 5.开始监听：select开头的方法
 **************************************************** **/
class SelectSocket
{
public:
    /* data */
    fd_set readfds;
    fd_set writefds;
    fd_set exceptfds;
    fd_set work_readfds;
    fd_set work_writefds;
    fd_set work_exceptfds;
    int max_fd;

public:
    SelectSocket(/* args */);
    // 添加对某个socket的等待，注意只需设置一次后续即可反复使用
    void setRead(const SocketBase &);
    void setWrite(const SocketBase &);
    void setExcept(const SocketBase &);
    // 清除对某个socket的等待
    void clearRead(const SocketBase &);
    void clearWrite(const SocketBase &);
    void clearExcept(const SocketBase &);
    // 检查某个socket的等待
    int checkRead(const SocketBase &);
    int checkWrite(const SocketBase &);
    int checkExcept(const SocketBase &);
    // 重置所有等待的socket
    void resetRead();
    void resetWrite();
    void resetExcept();
    void resetAll();
    // 开始等待
    int selectRead(struct timeval * = NULL);
    int selectWrite(struct timeval * = NULL);
    int selectExcept(struct timeval * = NULL);
    int selectAll(struct timeval * = NULL);

    ~SelectSocket();
};

/*******************************************************
 * SocketAccept类
 * 类说明：server端通讯socket的封装
 * 继承：继承自SocketBase
 * 功能：暂未增加新功能
 **************************************************** **/
class SocketAccept : public SocketBase
{
protected:
    /* data */
    friend class SocketListen; // 声明友元类

public:
    SocketAccept(/* args */);
    ~SocketAccept();
};

/*******************************************************
 * SocketListen类
 * 类说明：server端监听连接socket的封装，是服务端主socket
 * 继承：继承自SocketBase
 * 功能：
 * 1.绑定固定ip和端口
 * 2.开启监听
 * 3.接受连接
 **************************************************** **/
class SocketListen : public SocketBase
{
protected:
public:
    SocketListen(/* args */);

    // 服务端绑定网络序ip和网络序port
    int bindAddr(int, short);
    // 服务端绑定点分十进制ip和主机序port
    int bindAddr(const std::string &, short);
    // 服务端开启监听，传入监听队列长度（默认20）
    int listenSock(int = 20);
    // 同步接受请求socket
    int acceptSockSync(SocketAccept &);
    // 异步接受请求socket
    int acceptSockAsync(SocketAccept &, struct timeval * = NULL);
    /* 服务端接受连接
       - 同步/异步取决于是否调用了setAsync
       - 如果是同步，进入后直接阻塞，直到accept返回
       - 如果是异步则支持传入超时时间，默认为无限时间
       - 传入的第一个参数为返回的通讯socket
       - 特别注意，返回的通讯socket是同步，如果需要异步则手动开启 */
    int acceptSock(SocketAccept &, struct timeval * = NULL);

    ~SocketListen();
};

/*******************************************************
 * SocketConnect类
 * 类说明：client端socket的封装，是客户端主socket
 * 继承：继承自SocketBase
 * 功能：
 * 1.绑定固定ip和端口
 * 2.连接到指定ip和端口
 **************************************************** **/
class SocketConnect : public SocketBase
{
private:
    /* data */
public:
    SocketConnect(/* args */);

    // 客户端绑定主机序port
    int bindAddr(short);
    // 客户端连接指定点分十进制ip地址和主机序端口，如果异步则支持传入超时时间，默认无限
    int connectSock(const std::string &, short, struct timeval * = NULL);
    // 客户端绑定指定网络序ip地址和网络序端口，如果异步则支持传入超时时间，默认无限
    int connectSock(int, short, struct timeval * = NULL);

    ~SocketConnect();
};